#include "planner/properties.h"

#include "common/hash_util.h"
#include "common/node_utils.h"
#include "planner/property.h"
#include "planner/property_visitor.h"

namespace optimizer {

/**
 * Checks whether this is greater than or equal to another property.
 * If the other property is not a sort property, this function returns FALSE.
 * This property ensures that Sort(a,b,c,d,e) >= Sort(a,b,c)
 *
 * @param r other property to compare against
 * @returns TRUE if this >= r
 */
bool PropertySort::operator>=(const Property &r) const {
  // check the type
  if (r.Type() != PropertyType::SORT)
    return false;
  const PropertySort &r_sort = *reinterpret_cast<const PropertySort *>(&r);

  // All the sorting orders in r must be satisfied
  size_t l_num_sort_columns = list_length(sort_columns_);
  size_t r_num_sort_columns = list_length(r_sort.sort_columns_);
  CASCADES_ASSERT(r_num_sort_columns == r_sort.sort_ascending_.size(),
                  "Sort property num_sort_columns not match sort_ascending_.size()");

  // We want to ensure that Sort(a, b, c, d, e) >= Sort(a, b, c)
  if (l_num_sort_columns < r_num_sort_columns) {
    return false;
  }

  for (int idx = 0; idx < r_num_sort_columns; ++idx) {
    auto *x = list_nth(sort_columns_, idx);
    auto *y = list_nth(r_sort.sort_columns_, idx);
    if (!equal(x, y))
      return false;
  }
  return true;
}

/**
 * Hashes this PropertySort
 * @returns Hash code
 */
hash_t PropertySort::Hash() const {
  // hash the type
  hash_t hash = Property::Hash();

  // hash sorting columns
  size_t num_sort_columns = list_length(sort_columns_);
  for (int i = 0; i < num_sort_columns; ++i) {
    hash = HashUtil::CombineHashes(hash, NodeUtils::ExprHash(list_nth(sort_columns_, i)));

    auto asc_hash = HashUtil::Hash<OrderByOrderingType>(sort_ascending_[i]);
    hash = HashUtil::CombineHashes(hash, asc_hash);
  }
  return hash;
}

/**
 * PropertySort's Accept function for the visitor
 * @param v Visitor
 */
void PropertySort::Accept(PropertyVisitor *v) const {
  v->Visit(this);
}

std::string PropertySort::ToString() const {
  std::string str = "Sort(";
  // for (size_t i = 0; i < sort_columns_.size(); ++i) {
  //   str += sort_columns_[i]->ToString();
  //   if (i < sort_ascending_.size()) {
  //     str += (sort_ascending_[i] == OrderByOrderingType::ASC) ? " ASC" : " DESC";
  //   }
  //   if (i < sort_columns_.size() - 1) {
  //     str += ", ";
  //   }
  // }
  return str + ")";
}

}  // namespace optimizer
